﻿/** \file
* \$Rev: 107 $
* 
* \$Date: 2011-04-12 23:22:43 +0000 (Tue, 12 Apr 2011) $
*
* \$URL: http://benfwaves.googlecode.com/svn/branches/fftw/FFTW/Plan.cs $
*/

using System;
using System.Runtime.InteropServices;

namespace FFTW
{
	/// <summary>
	/// The FFTW plan creation flags.
	/// </summary>
	[Flags]
	public enum PlanFlags
	{
		Measure = 0,
		DestroyInput = 1 << 0,
		Unaligned = 1 << 1,
		ConserveMemory = 1 << 2,
		Exhaustive = 1 << 3, /* NO_EXHAUSTIVE is default */
		PreserveInput = 1 << 4, /* cancels FFTW_DESTROY_INPUT */
		Patient = 1 << 5, /* IMPATIENT is default */
		Estimate = 1 << 6,

		/* undocumented beyond-guru flags */
		EstimatePatient = 1 << 7,
		BelievePCost = 1 << 8,
		NoDftR2HC = 1 << 9,
		NoNonthreaded = 1 << 10,
		NoBuffering = 1 << 11,
		NoIndirectOp = 1 << 12,
		AllowLargeGeneric = 1 << 13, /* NO_LARGE_GENERIC is default */
		NoRankSplits = 1 << 14,
		NoVrankSplits = 1 << 15,
		NoVRecurse = 1 << 16,
		NoSIMD = 1 << 17,
		NoSlow = 1 << 18,
		NoFixedRadixLargeN = 1 << 19,
		AllowPruning = 1 << 20,
		WisdomOnly = 1 << 21
	};

	/// <summary>
	/// The type of real-to-real transform.
	/// </summary>
	public enum R2RKind
	{
		RealToHalfComplex,
		HalfComplexToReal,
		DiscreteHartleyTransform,
		REDFT00,
		REDFT01,
		REDFT10,
		REDFT11,
		RODFT00,
		RODFT01,
		RODFT10,
		RODFT11
	};

	/// <summary>
	/// The transform direction (time to frequency or frequency to time).
	/// </summary>
	public enum Direction
	{
		Forward = 1,
		Reverse = -1
	};

	/// <summary>
	/// The base type for all FFTW plans.
	/// </summary>
	/// <typeparam name="ElementType">Float or double.</typeparam>
	public abstract class Plan<ElementType> : SafeHandle
		where ElementType : struct
	{
		/// <summary>
		/// Get the right delegate for an FFTW p/invoke call based on whether
		/// this plan is for float or double.
		/// </summary>
		/// <typeparam name="DelType">The delegate type.</typeparam>
		/// <param name="choices">The delegate choice object.</param>
		/// <returns>The delegate.</returns>
		protected static DelType GetDel<DelType>(DelChoices<DelType> choices)
		{
			return choices.GetDel<ElementType>();
		}

		/// <summary>
		/// The plan constructor.
		/// </summary>
		public Plan()
			: base(IntPtr.Zero, true) { }

		/// <summary>
		/// Whether the plan handle is valid.
		/// </summary>
		public override bool IsInvalid
		{
			get { return handle == IntPtr.Zero; }
		}

		/// <summary>
		/// Execute the current plan.
		/// </summary>
		public void Execute()
		{
			GetDel(PInvoke.execute)(handle);
		}

		/// <summary>
		/// Clean up all FFTW resources (not just for one plan).
		/// </summary>
		public static void Cleanup()
		{
			GetDel(PInvoke.cleanup)();
		}

		/// <summary>
		/// Destroy this plan.
		/// </summary>
		/// <returns>True if the plan was successfully destroyed.</returns>
		protected override bool ReleaseHandle()
		{
			GetDel(PInvoke.destroy_plan)(handle);
			return true;
		}
	}

	/// <summary>
	/// A complex-to-complex discrete Fourier transform.
	/// </summary>
	/// <typeparam name="ElementType">Float or double.</typeparam>
	public class Plan_C2C<ElementType> : Plan<ElementType>
		where ElementType : struct
	{
		/// <summary>
		/// The input array.
		/// </summary>
		public readonly AlignedArrayComplex<ElementType> ArrayIn;

		/// <summary>
		/// The output array.
		/// </summary>
		public readonly AlignedArrayComplex<ElementType> ArrayOut;

		/// <summary>
		/// Construct a new complex-to-complex plan.
		/// </summary>
		/// <param name="arrayIn">The input array.</param>
		/// <param name="arrayOut">The output array.</param>
		/// <param name="flags">The plan creation flags.</param>
		/// <param name="direction">Whether the transform is forward or reverse.</param>
		public Plan_C2C(
			AlignedArrayComplex<ElementType> arrayIn,
			AlignedArrayComplex<ElementType> arrayOut,
			PlanFlags flags,
			Direction direction)
		{
			this.ArrayIn = arrayIn;
			this.ArrayOut = arrayOut;
			handle = GetDel(PInvoke.plan_dft_1d)
				(arrayIn.count,
				arrayIn.DangerousGetHandle(),
				arrayOut.DangerousGetHandle(),
				direction, flags);
		}
	}

	/// <summary>
	/// A complex-to-real transform.
	/// </summary>
	/// <typeparam name="ElementType">Float or double.</typeparam>
	public class Plan_C2R<ElementType> : Plan<ElementType>
		where ElementType : struct
	{
		/// <summary>
		/// The input array.
		/// </summary>
		public readonly AlignedArrayComplex<ElementType> ArrayIn;

		/// <summary>
		/// The output array.
		/// </summary>
		public readonly AlignedArrayReal<ElementType> ArrayOut;

		/// <summary>
		/// Construct a new complex-to-real transform.
		/// </summary>
		/// <param name="arrayIn">The input array.</param>
		/// <param name="arrayOut">The output array.</param>
		/// <param name="flags">The plan creation flags.</param>
		public Plan_C2R(
			AlignedArrayComplex<ElementType> arrayIn,
			AlignedArrayReal<ElementType> arrayOut,
			PlanFlags flags)
		{
			this.ArrayIn = arrayIn;
			this.ArrayOut = arrayOut;
			handle = GetDel(PInvoke.plan_dft_c2r_1d)
				(arrayIn.count,
				arrayIn.DangerousGetHandle(),
				arrayOut.DangerousGetHandle(),
				flags);
		}
	}

	/// <summary>
	/// A real-to-complex transform plan.
	/// </summary>
	/// <typeparam name="ElementType">Float or double.</typeparam>
	public class Plan_R2C<ElementType> : Plan<ElementType>
		where ElementType : struct
	{
		/// <summary>
		/// The input array.
		/// </summary>
		public readonly AlignedArrayReal<ElementType> ArrayIn;
		/// <summary>
		/// The output array.
		/// </summary>
		public readonly AlignedArrayComplex<ElementType> ArrayOut;

		/// <summary>
		/// Construct a new real-to-complex transform plan.
		/// </summary>
		/// <param name="arrayIn">The input array.</param>
		/// <param name="flags">The plan creation flags.</param>
		public Plan_R2C(
			AlignedArrayReal<ElementType> arrayIn,
			PlanFlags flags)
		{
			this.ArrayIn = arrayIn;
			this.ArrayOut = new AlignedArrayComplex<ElementType>(
				arrayIn.count / 2 + 1);
			handle = GetDel(PInvoke.plan_dft_r2c_1d)
				(ArrayIn.count,
				ArrayIn.DangerousGetHandle(),
				ArrayOut.DangerousGetHandle(),
				flags);
		}
	}

	/// <summary>
	/// A real-to-real transform plan.
	/// </summary>
	/// <typeparam name="ElementType">Float or double.</typeparam>
	public class Plan_R2R<ElementType> : Plan<ElementType>
		where ElementType : struct
	{
		/// <summary>
		/// The input array.
		/// </summary>
		public readonly AlignedArrayReal<ElementType> ArrayIn;
		/// <summary>
		/// The output array.
		/// </summary>
		public readonly AlignedArrayReal<ElementType> ArrayOut;

		/// <summary>
		/// Construct a new real-to-real transform plan.
		/// </summary>
		/// <param name="arrayIn">The input array.</param>
		/// <param name="arrayOut">The output array.</param>
		/// <param name="kind">The real-to-real transform kind.</param>
		/// <param name="flags">The plan creation flags.</param>
		public Plan_R2R(
			AlignedArrayReal<ElementType> arrayIn,
			AlignedArrayReal<ElementType> arrayOut,
			R2RKind kind, PlanFlags flags)
		{
			this.ArrayIn = arrayIn;
			this.ArrayOut = arrayOut;
			handle = GetDel(PInvoke.plan_r2r_1d)
				(arrayIn.count,
				arrayIn.DangerousGetHandle(),
				arrayOut.DangerousGetHandle(),
				kind, flags);
		}
	}
}
